package com.zzyl.nursing.service.impl;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.common.utils.DateUtils;
import com.zzyl.common.utils.StringUtils;
import com.zzyl.nursing.config.WebSocketServer;
import com.zzyl.nursing.domain.AlertData;
import com.zzyl.nursing.domain.DeviceData;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.service.IAlertDataService;
import com.zzyl.nursing.vo.AlertNotifyVo;
import com.zzyl.system.mapper.SysUserRoleMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import com.zzyl.nursing.mapper.AlertRuleMapper;
import com.zzyl.nursing.domain.AlertRule;
import com.zzyl.nursing.service.IAlertRuleService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.util.NumberUtils;

import java.util.concurrent.TimeUnit;

/**
 * 报警规则Service业务层处理
 *
 * @author ruoyi
 * @date 2024-12-18
 */
@Service
public class AlertRuleServiceImpl extends ServiceImpl<AlertRuleMapper, AlertRule> implements IAlertRuleService {
    @Autowired
    private AlertRuleMapper alertRuleMapper;

    /**
     * 查询报警规则
     *
     * @param id 报警规则主键
     * @return 报警规则
     */
    @Override
    public AlertRule selectAlertRuleById(Long id) {
        return getById(id);
    }

    /**
     * 查询报警规则列表
     *
     * @param alertRule 报警规则
     * @return 报警规则
     */
    @Override
    public List<AlertRule> selectAlertRuleList(AlertRule alertRule) {
        return alertRuleMapper.selectAlertRuleList(alertRule);
    }

    /**
     * 新增报警规则
     *
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int insertAlertRule(AlertRule alertRule) {
        return save(alertRule) ? 1 : 0;
    }

    /**
     * 修改报警规则
     *
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int updateAlertRule(AlertRule alertRule) {
        return updateById(alertRule) ? 1 : 0;
    }

    /**
     * 批量删除报警规则
     *
     * @param ids 需要删除的报警规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleByIds(Long[] ids) {
        return removeByIds(Arrays.asList(ids)) ? 1 : 0;
    }

    /**
     * 删除报警规则信息
     *
     * @param id 报警规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleById(Long id) {
        return removeById(id) ? 1 : 0;
    }

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private DeviceMapper deviceMapper;
    @Autowired
    private SysUserRoleMapper sysUserRoleMapper;

    @Autowired
    private IAlertDataService alertDataService;

    //维修工
    @Value("${alert.deviceMaintainerRole}")
    private String deviceMaintainerRole;
    //管理员
    @Value("${alert.managerRole}")
    private String managerRole;

    /**
     * 报警规则过滤
     */
    @Override
    public void alertFilter() {
        //1.去查询alert_rule表,看规则是否生效,不生效直接return
        if (count(Wrappers.<AlertRule>lambdaQuery().eq(AlertRule::getStatus, 1)) == 0) {
            return;
        }
        //2.去redis数据库中查询设备数据，如果设备数据不存在，直接return
        List deviceDataList = redisTemplate.opsForHash().values(CacheConstants.IOT_DEVICEDATA_LATEST);
        if (CollectionUtils.isEmpty(deviceDataList)) {
            return;
        }
        //3.解析每一条上报的数据,存放到新的集合中
        List<DeviceData> newDeviceData = new ArrayList<>();
        deviceDataList.forEach(deviceDataStr -> {
            newDeviceData.addAll(JSONUtil.toList(deviceDataStr.toString(), DeviceData.class));
        });
        //4.获取设备的报警规则(分为两种,一种是全部设备的报警规则,一种是单个设备的报警规则)
        newDeviceData.forEach(deviceData -> {
            List<AlertRule> deviceList = null;
            //4.1.获取全部设备的报警规则
            LambdaQueryWrapper<AlertRule> wrapper = Wrappers.lambdaQuery();
            wrapper.eq(AlertRule::getIotId, "-1")
                    .eq(AlertRule::getFunctionId, deviceData.getFunctionId())
                    .eq(AlertRule::getProductKey, deviceData.getProductKey());
            deviceList = list(wrapper);
            //4.2.获取单个设备的报警规则
            wrapper.clear();
            wrapper.eq(AlertRule::getIotId, deviceData.getIotId())
                    .eq(AlertRule::getFunctionId, deviceData.getFunctionId())
                    .eq(AlertRule::getProductKey, deviceData.getProductKey());
            //4.3.将报警规则合二为一
            deviceList.addAll(list(wrapper));
            if (CollectionUtils.isEmpty(deviceList)) {
                //如果设备上报的数据,并没有报警规则,就跳过这个上报数据
                return;
            }
            //遍历所有规则(由于业务不明确,我们要考虑到同一个设备的功能可能有多个规则,我们遍历规则,一个一个判断,只要满足都算)
            deviceList.forEach(alertRule -> {
                jiaoyanAndInsert(deviceData, alertRule);
            });
        });

    }

    private void jiaoyanAndInsert(DeviceData deviceData, AlertRule alertRule) {
        // 统计次数的key
        String aggCountKey = CacheConstants.ALERT_TRIGGER_COUNT_PREFIX + deviceData.getIotId() + ":" + deviceData.getFunctionId() + ":" + alertRule.getId();
        //第一次校验(判断规则的生效时间,如果不在生效时间之间,则会跳出)
        String betweenTimeStr = alertRule.getAlertEffectivePeriod();
        String[] split = betweenTimeStr.split("~");
        LocalTime startTime = LocalTime.parse(split[0]);
        LocalTime endTime = LocalTime.parse(split[1]);
        LocalTime currentTime = deviceData.getAlarmTime().toLocalTime();
        if (currentTime.isBefore(startTime) || currentTime.isAfter(endTime)) {
            return;
        }
        //第二次校验(判断规则的阈值)
        Double deviceValue = Double.valueOf(deviceData.getDataValue());
        Double alertValue = alertRule.getValue();
        String operator = alertRule.getOperator();
        int compareToResult = deviceValue.compareTo(alertValue);
        if (">=".equals(operator)) {
            if (!(compareToResult > 0)) {
                //不满足报警条件,清除之前的报警次数
                redisTemplate.delete(aggCountKey);
                return;
            }
        } else {
            //"<"
            if (compareToResult > 0) {
                //不满足报警条件,清除之前的报警次数
                redisTemplate.delete(aggCountKey);
                return;
            }
        }
        //第三次校验(判断报警周期的次数)
        Integer count = (Integer) redisTemplate.opsForValue().get(aggCountKey);
        count = count == null ? 1 : ++count;
        if (count < alertRule.getDuration()) {
            redisTemplate.opsForValue().set(aggCountKey, count);
            return;
        }
        //如果达到报警情况,需要将报警次数清零
        redisTemplate.opsForValue().set(aggCountKey, 0);
        //第四次校验(判断沉默周期)
        //去数据库中查询沉默周期,存在就跳过
        String alertSilentKey = CacheConstants.ALERT_SILENT_PREFIX + deviceData.getIotId() + ":" + deviceData.getFunctionId() + alertRule.getId();
        Object o = redisTemplate.opsForValue().get(alertSilentKey);
        if (o != null) {
            return;
        }
        //都符合校验规则后,就需要添加沉默周期了(为了下一次做校验准备)
        Long alertSilentPeriod = alertRule.getAlertSilentPeriod();
        redisTemplate.opsForValue().set(alertSilentKey, "我爱敲代码", alertSilentPeriod, TimeUnit.MINUTES);
        //确定需要报警后,我们需要根据情况通知护理员以及管理员以及维修工
        //通知后,我们需要将数据插入到表中
        Integer alertDataType = alertRule.getAlertDataType();
        List<Long> userId = null;
        if (alertDataType == 0) {
            //老人异常数据(通知护工以及超级管理员)
            if (deviceData.getLocationType() == 0) {
                //随身设备
                userId = deviceMapper.selectNursingIdsByIotIdWithElder(deviceData.getIotId());
            } else if (deviceData.getLocationType() == 1 && deviceData.getPhysicalLocationType() == 2) {
                //固定的床位设备
                userId = deviceMapper.selectNursingIdsByIotIdWithBed(deviceData.getIotId());
            }
            System.out.println("userId护理员 = " + userId);
        } else {
            //设备异常数据,需要通知维修工
            userId = sysUserRoleMapper.selectUserIdByRoleName(deviceMaintainerRole);

        }
        //一定要通知超级管理员的
        List<Long> managerUserIds = sysUserRoleMapper.selectUserIdByRoleName(managerRole);
        System.out.println("userId超级管理员 = " + managerUserIds);
        //合并用户id
        Collection<Long> allUserIds = CollUtil.addAll(userId, managerUserIds);
        //合并之后与可能有重复id,去重
        allUserIds = CollUtil.distinct(allUserIds);
        System.out.println("userId合并后 = " + allUserIds);
        List<AlertData> alertDataList = insertAlertData(deviceData, alertRule, allUserIds);
        // websocket推送消息
        websocketNotity(alertDataList.get(0), alertRule, allUserIds);
    }


    @Autowired
    private WebSocketServer webSocketServer;

    /**
     * websocket推送消息
     *
     * @param alertData
     * @param alertRule
     * @param allUserIds
     */
    private void websocketNotity(AlertData alertData, AlertRule alertRule, Collection<Long> allUserIds) {
        // 属性拷贝
        AlertNotifyVo alertNotifyVo = BeanUtil.toBean(alertData, AlertNotifyVo.class);
        alertNotifyVo.setFunctionName(alertRule.getFunctionName());
        alertNotifyVo.setAlertDataType(alertRule.getAlertDataType());
        alertNotifyVo.setNotifyType(1);
        // 向指定的人推送消息
        webSocketServer.sendMessageToConsumer(alertNotifyVo, allUserIds);

    }

    /**
     * 批量插入警告数据
     *
     * @param deviceData
     * @param alertRule
     * @param allUserIds
     */
    private List<AlertData> insertAlertData(DeviceData deviceData, AlertRule alertRule, Collection<Long> allUserIds) {
        //遍历所有id,每一个id都要往警告通知表中插入一条数据
        List<AlertData> list = new ArrayList();
        allUserIds.forEach(id -> {
            //对象拷贝
            AlertData alertData = BeanUtil.toBean(deviceData, AlertData.class);
            alertData.setAlertRuleId(alertRule.getId());
            // 心率<60,持续3个周期就报警
            String alertReason = CharSequenceUtil.format("{}{}{},持续{}个周期就报警", alertRule.getFunctionName(), alertRule.getOperator(), alertRule.getValue(), alertRule.getDuration());
            alertData.setAlertReason(alertReason);
            alertData.setCreateTime(new Date());
            alertData.setStatus(0);
            alertData.setType(alertRule.getAlertDataType());
            alertData.setUserId(id);
            alertData.setId(null);
            list.add(alertData);
        });
        alertDataService.saveBatch(list);
        return list;
    }


}
